/*=====================================================================
CONFIGURATION SYSTEMS
=====================================================================*/

local settings = {}

/*
	EXAMPLE:

name arguments
name arguments
etc
*/

function loadSettings()
	if not file.Exists("moocow/settings.txt") then
		file.Write("moocow/settings.txt", "")
	end
	local lines = string.Explode("\n", file.Read("moocow/settings.txt"))
	for i, ln in pairs(lines) do
		ln = string.lower(ln)
		local parts = string.Explode(" ", ln)
		if table.Count(parts) >= 2 then
			local name = parts[1]
			table.remove(parts, 1)
			settings[name] = table.concat(parts, " ")
		end
	end
	if not settings["command_sequence"] then
		settings["command_sequence"] = "/"
	end
end

function getMOOSettings()
	return settings
end

/*=====================================================================
PLUGIN SYSTEMS
=====================================================================*/

local commands = {}
local commandsDesc = {}

local function isDirectory(name)
	if string.find(name, ".", 1, true) == nil then
		return true
	else
		return false
	end
end

local function getEndName(name)
	local lastSlash = 1
	for i, char in pairs(string.ToTable(name)) do
		if char == "/" then
			lastSlash = i
		end
	end
	return string.sub(name, lastSlash, string.len(name))
end

function getPluginPathByName(base, name)
	local list = file.FindInLua(base .. "*")
	for i, thefile in pairs(list) do
		if string.sub(thefile, 1, 1) != "." then
			if not isDirectory(base .. thefile) then
				if name == getEndName(thefile) then
					return base .. thefile
				end
			else
				getPluginPathByName(base .. thefile .. "/", name)
			end
		end
	end
end

function includeAllPlugins(base)
	local list = file.FindInLua(base .. "*")
	for i, thefile in pairs(list) do
		if string.sub(thefile, 1, 1) != "." then
			if not isDirectory(base .. thefile) then
				print("MOOCOW_LOADED: " .. base .. thefile)
				include(base .. thefile)
				AddCSLuaFile(base .. thefile)
			else
				includeAllPlugins(base .. thefile .. "/")
			end
		end
	end
end

function registerCommand(name, description, func)
	commands[name] = func
	commandsDesc[name] = description
end

local function sendConCommands(ply)
	for name, cmd in pairs(commands) do
		umsg.Start("moocow_registercommand_client", { ply })
			umsg.String(name)
		umsg.End()
	end
end
hook.Add("PlayerInitialSpawn", "moocow_sendconcommands", sendConCommands)

local function getDashes(times)
	local dash = ""
	for i = 1, times do
		dash = dash .. "-"
	end
	return dash
end

function executeFunc(name, param)
	local found = false
	if commands[name] then
		found = true
		if (not param[1]:IsValid()) || playerCanUsePlugin(param[1], name) then
			param["settings"] = settings
			ret = commands[name](param)
			if not ret then
				ret = ""
			end
			return ret
		else
			param[1]:ConCommand("echo Dont have required access level")
		end
	end
	if found == false then
		param[1]:ConCommand("echo Command doesnt exist")
	end
	return ""
end

function getCaller(ply)
	if ply:IsValid() then
		return ply:GetName()
	else
		return "Server"
	end
end

/*=====================================================================
TEAM SYSTEMS
=====================================================================*/

local teams = {}

registerCommand("help", "Displays all commands with descriptions", function(param)
	local ranksByNumber = {}
	local count = 0
	for i, rank in pairs(teams) do
		ranksByNumber[count] = rank["name"]
		count = count + 1
	end
	local cmdOrder = {}
	
	table.sortdesc(ranksByNumber)
	local rankAmount = table.Count(ranksByNumber)
	local isClient = param[1]:IsValid()
	for i = 1, rankAmount do
		for k, v in pairs(teams[ranksByNumber[rankAmount - i]].commands) do
			local desc = commandsDesc[k]
			local theRank = ranksByNumber[rankAmount - i]
			if desc then
				local cmd = k .. " " .. getDashes(15 - string.len(k)) .. " " .. theRank ..  " " .. getDashes(20 - string.len(theRank)) .. " " .. tostring(desc)// .. "\n"
				if isClient then
					param[1]:ConCommand("echo " .. tostring(cmd))
				else
					print(tostring(cmd))
				end
			end
		end
	end
	if isClient then
		param[1]:PrintMessage(HUD_PRINTTALK, "Look in console for the commands")
	end
end)

/*
	TODO: MAKE CHECKS TO ENSURE THAT THERE IS NO INFINITE LOOP OF INHERITANCE!
	EXAMPLE TEAM:
Name: Rawr
Inherits: Moo
Color: 255 255 255 255 - Dunno how to do
Commands:
	slay console(server/caller/target) log(server)
	not kick

Whom types
	all
	server
	caller
	target
*/

local function getDisplaysName(cmd)
//Will parse rawr(hey) to be rawr
	local paren = string.find(cmd, "(", 1, true)
	if paren != nil then
		return string.sub(cmd, 1, paren - 1)
	else
		return cmd
	end
end

local function getDisplaysParams(cmd)
	local paren = string.find(cmd, "(", 1, true)
	if paren != nil then
		local endParen = string.find(cmd, ")", paren, true)
		if endParen != nil then
			local params = string.sub(cmd, paren + 1, endParen - 1)
			return string.Explode("/", params)
		else
			return { "all" }
		end
	else
		return { "all" }
	end
end

function loadTeams()
	if not file.Exists("moocow/teams.txt") then
		file.Write("moocow/teams.txt", "")
	end
	local mess = string.lower(file.Read("moocow/teams.txt"))
	local lines = string.Explode("\n", mess)

	local stage = 0
	local build = {}
	local commands = {}
	for i, ln in pairs(lines) do
		ln = string.Trim(string.Trim(ln), "\t")
		local split = string.Explode(" ", ln)
		if table.Count(split) < 1 then
			return false
		elseif split[1] == "name:" then
			if stage == 0 && table.Count(split) >= 2 then
				stage = stage + 1
				build["name"] = split[2]
			elseif stage == 4 && table.Count(split) >= 2 then
				build["commands"] = commands
				teams[build["name"]] = build
				build = {}
				stage = 1
				build["name"] = split[2]
			else
				return false
			end
		elseif split[1] == "inherits:" then
			if stage == 1 && table.Count(split) >= 2 then
				stage = stage + 1
				build["inherits"] = split[2]
			else
				return false
			end
		elseif split[1] == "color:" then
			if stage == 1 || stage == 2 then
				if stage == 1 then
					stage = stage + 1
					build["inherits"] = "client"
				end

				if table.Count(split) >= 5 then
					stage = stage + 1
					team.SetUp(table.Count(teams) + 1, string.Replace(build["name"], "_", " "), Color(tonumber(split[2]), tonumber(split[3]), tonumber(split[4]), tonumber(split[5])))
				else
					return false
				end
			end
		elseif split[1] == "commands:" then
			if stage == 3 then
				stage = stage + 1
				commands = {}
			else
				return false
			end
		elseif stage == 4 then
			//slay console(server/caller/target) log(server)
			if split[1] != "not" then
				local name = split[1]
				local methods = {}
				if settings["default_display"] then
					for i, method in pairs(string.Explode(" ", settings["default_display"])) do
						methods[getDisplaysName(method)] = getDisplaysParams(method)
					end
				end
				table.remove(split, 1)
				for i, method in pairs(split) do
					methods[getDisplaysName(method)] = getDisplaysParams(method)
				end
				commands[name] = methods
			elseif table.Count(split) >= 2 then
				commands[split[2]] = { "not" }
			end
		end
	end
	build["commands"] = commands
	teams[build["name"]] = build
end

function playerCanUsePlugin(ply, plugin)
	local rank = getRank(ply)
	return rankHasPlugin(rank, plugin)
end

function getRankWithPlugin(plugin)
	for i, t in pairs(teams) do
		if t["commands"][plugin] then
			return i
		end
	end
	return ""
end

function rankHasPlugin(rank, plugin)
	plugin = string.lower(plugin)
	local commands = rank["commands"]
	if commands[plugin] then
		if commands[plugin] != "remove" then
			return true
		else
			return false
		end
	elseif rank["name"] != "client" then
		if teams[rank["inherits"]] then
			return rankHasPlugin(teams[rank["inherits"]], plugin)
		end
	end
	return false
end

function rankIsA(rank, otherrank)
	if rank["name"] == otherrank["name"] then
		return true
	elseif rank["name"] != "client" then
		if teams[rank["inherits"]] then
			return rankIsA(teams[rank["inherits"]], otherrank)
		end
	else
		return false
	end
end

function getRank(ply)
	if file.Exists("moocow/users.txt") then
		local ID = string.lower(ply:SteamID())
		local mess = string.lower(file.Read("moocow/users.txt"))
		local lines = string.Explode("\n", mess)
		
		for i, ln in pairs(lines) do
			local split = string.Explode(" ", ln)
			if table.Count(split) >= 2 then
				if ID == split[1] then
					if teams[split[2]] then
						return teams[split[2]]
					else
						return teams["client"]
					end
				end
			end
		end
	end
	return teams["client"]
end

function getRankByStr(name)
	for rank, tbl in pairs(teams) do
		if name == rank then
			return tbl
		end
	end
end

/*=====================================================================
TEAM SYSTEMS
=====================================================================*/

local displayMethods = {}

function registerDisplay(name, func)
	if name != "not" then
		displayMethods[name] = func
	end
end

function display(caller, plugin, who, what)
	local players = {}
	for i, ply in pairs(who) do
		if ply:IsPlayer() then
			players[i] = ply
		end
	end
	
	local rankToCheck = getRankByStr(getRankWithPlugin(plugin))
	if not rankToCheck then
		return nil
	end
	
	if caller:IsValid() then
		rankToCheck = getRank(caller)
	end
	local plugDispsAs = pluginDisplaysAs(rankToCheck, plugin)
		//Find display to whom setting
		/*
			Whom types
				all
				server
				caller
				target
		*/
	if plugDispsAs then
		local displayTo = {}
		if not plugDispsAs["all"] then
			for i, displayMethod in pairs(plugDispsAs) do //displayMethod = 1, 2, 3 etc
				for x, toWhom in pairs(displayMethod) do //toWhom = displayMethod = all/caller/target/server
					if toWhom == "all" then
						displayTo = player.GetAll()
						displayTo["server"] = true
					elseif toWhom == "server" then
						displayTo["server"] = true
					elseif toWhom == "caller" then
						displayTo["caller"] = caller
					elseif toWhom == "target" then
						for y, theply in pairs(players) do
							if theply then
								displayTo["target" .. tostring(y)] = theply
							end
						end
					end
				end
				if displayMethods[i] then
					displayMethods[i](displayTo, what)
				end
				displayTo = {}
			end
		else
			displayMethod = plugDispsAs["all"]
			for i, toWhom in pairs(displayMethod) do
				if toWhom == "all" then
					displayTo = player.GetAll()
					displayTo["server"] = true
				elseif toWhom == "server" then
					displayTo["server"] = true
				elseif toWhom == "caller" then
					displayTo["caller"] = caller
				elseif toWhom == "target" then
					for x, theply in pairs(players) do
						if theply then
							displayTo["target" .. tostring(x)] = theply
						end
					end
				end
			end
			for i, dispMethod in pairs(displayMethods) do
				dispMethod(displayTo, what)
			end
		end
	end
end

function getPlayerByName(name)
	name = string.lower(name)
	local toMatch = string.sub(name, 1, 1)
	if toMatch != "" && (not string.match(toMatch, "%d")) && (not string.match(toMatch, " ")) then
		local allPlayers = player.GetAll()
		for i, ply in pairs(allPlayers) do
			if string.find(string.lower(ply:GetName()), name) then
				return ply
			end
		end
	elseif string.match(toMatch, "%d") then
		local allPlayers = player.GetAll()
		for i, ply in pairs(allPlayers) do
			if name == tostring(ply:UserID()) then
				return ply
			end
		end
	end
	return nil
end

function pluginDisplaysAs(rank, plugin)
	local commands = rank["commands"]
	if commands[plugin] then
		if commands[plugin] != "remove" then
			return commands[plugin]
		else
			return false
		end
	elseif rank["name"] != "client" then
		if teams[rank["inherits"]] then
			return pluginDisplaysAs(teams[rank["inherits"]], plugin)
		end
	end
	return false
end

function parseDisplayMessage(message, toReplace)
	local build = message
	for i, with in pairs(toReplace) do
		build = string.Replace(build, tostring(i), tostring(with))
	end
	return build
end

/*=====================================================================
DATA PASS SYSTEMS
	This section has functions that allow data to be transfered between plugins
	This section is unused by all default plugins
	Using this function in a custom plugin can result in issues if the plugins attempt to transfer data from an unitiailised plugin
=====================================================================*/

local passData = {}

function setPassData(name, data)
	passData[name] = data
end

function getPassData(name)
	return passData[name]
end

/*=====================================================================
UTILITY SYSTEMS
=====================================================================*/

function tableRemoveByElement(tab, element)
	//Different because it removes the element, not the index
	local ret = {}
	for i, index in pairs(tab) do
		if index != element then
			ret[i] = index
		end
	end
end

function tableRemoveByStrIndex(tab, str)
	//Different because it removes string indexes
	local ret = {}
	for i, index in pairs(tab) do
		if i != str then
			ret[i] = index
		end
	end
end

function debugMOOCOW(player, command, arguments)
	print("Settings")
	PrintTable(settings)
	print("Commands")
	PrintTable(commands)
	print("Teams")
	PrintTable(teams)
	print("Display Methods")
	PrintTable(displayMethods)
end
concommand.Add("moocow_debug", debugMOOCOW)

loadSettings()
loadTeams()
includeAllPlugins("moocow_plugins/")
AddCSLuaFile("moocow_client.lua")

for i, ply in pairs(player.GetAll()) do
	sendConCommands(ply)
end